<?php

namespace util;

/**
 * 基础通信类
 *
 * @author Johnny Bai
 * @version $1.0
 *         
 * @author chenkeyu <chenkeyu@dangdang.com>
 * @version $2.0
 *         
 * @author anyuchao
 * @version $3.0
 *         
 * @author fuyou
 * @version 4.0
 */
class APIClient {
    /**
     * curl instance array
     *
     * @var array
     */
    private static $curlInstance = array ();
    /**
     * soap instance array
     *
     * @var array
     */
    private static $soapInstance = array ();
    /**
     * fetch the curl connection pointer
     *
     * @param string $id            
     * @param mixed $ch            
     * @since May 29, 2015 6:35:37 PM
     * @access
     *
     */
    private static function fetchCurlInstance($id) {
        if (! empty ( self::$curlInstance [$id] )) {
            $ch = self::$curlInstance [$id];
        } else {
            $ch = curl_init ();
            curl_reset ( $ch );
            self::$curlInstance [$id] = $ch;
        }
        return $ch;
    }
    /**
     * fetch the soap connection pointer
     *
     * @param string $id            
     * @param string $url            
     * @since May 29, 2015 8:45:47 PM
     * @access
     *
     */
    private static function fetchSoapInstance($id, $url) {
        if (! empty ( self::$soapInstance [$id] )) {
            $ch = self::$soapInstance [$id];
        } else {
            $ch = new \SoapClient ( $url, array (
                    'connection_timeout' => 3 
            ) );
            self::$soapInstance [$id] = $ch;
        }
        return $ch;
    }
    /**
     * http call only GET method
     * <code>
     * $Lib_APIClient = new Lib_APIClient();
     * $result3 = $Lib_APIClient::httpCall($name,$parameters);
     * </code>
     *
     * @param string $name
     *            url
     * @param array $arguments
     *            GET arguments
     * @param string $type
     *            ('xml','array','json')
     * @return array
     */
    public static function httpCall($name, $arguments, $type = "xml", $post_params = array(), $timeout = 3, $headers = array('content-type: application/x-www-form-urlencoded;charset=UTF-8')) {
        if (! in_array ( $type, array (
                'xml',
                'serl',
                'json',
                'string',
                'array',
                'xml_obj',
                'geturl' 
        ) )) {
            $type = 'xml';
        }
        if (! empty ( $arguments )) {
            if (is_array ( $arguments )) {
                $s = http_build_query ( $arguments ); // do rawurlencode here
            } else {
                $s = $arguments; // 针对某些不能urlencode传过去的参数 直接外面拼装参数... 比如搜索列表的filt_type...
            }
            $request_url = $name . "?$s";
        } else {
            $request_url = $name;
        }
        /**
         * 缓存curl连接
         */
        $id = $name . ':' . scp_get_thread_id ();
        $ch = self::fetchCurlInstance ( $id );
        curl_setopt ( $ch, CURLOPT_HTTPHEADER, $headers );
        curl_setopt ( $ch, CURLOPT_HEADER, 0 );
        curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 ); // Set curl to return the data instead of printing it to the browser.
        curl_setopt ( $ch, CURLOPT_URL, $request_url );
        curl_setopt ( $ch, CURLOPT_TIMEOUT, $timeout );
        if (! empty ( $post_params ) && is_array ( $post_params )) {
            curl_setopt ( $ch, CURLOPT_POST, 1 );
            curl_setopt ( $ch, CURLOPT_POSTFIELDS, http_build_query ( $post_params ) );
        }
        $try_time = 0;
        do {
            $result = curl_exec ( $ch );
            $try_time ++;
        } while ( ! $result && $try_time < 2 );
        $errno = curl_errno ( $ch );
        $error = curl_error ( $ch );
        if (! empty ( $errno ) || ! empty ( $error )) {
            $logInfo = 'URL  ' . $name . '  error ' . $errno . ':' . $error;
            soa_error ( $logInfo, '[pchttpinterfacefailed]' );
            unset ( self::$curlInstance [$id] );
            curl_close ( $ch ); // 连接错误断开关闭连接
        }
        if (0 == strlen ( $result )) {
            if ($type == 'string') {
                return "";
            } else {
                return array ();
            }
        }
        
        $result = trim ( $result ); // 必不可少...
        if ($type == "xml") {
            $result = \util\XMLUtil::xmlToArray ( $result );
            return $result;
        } elseif ($type == 'serl') {
            return unserialize ( $result );
        } elseif ($type == 'json') {
            return json_decode ( $result, true );
        } elseif ($type == 'string') {
            return $result;
        } elseif ($type == 'xml_obj') {
            $xml_string = \util\ComFunc::convertXmlEncode ( $result );
            $obj = simplexml_load_string ( $xml_string, 'SimpleXMLElement', LIBXML_NOCDATA );
            return $obj;
        }
        return array ();
    }
    
    /**
     * soap call
     *
     * @param string $wsUrl            
     * @param string $func            
     * @param string $arr            
     * @return array
     */
    public static function soapCall($wsUrl, $func, $arr, $xmlname = '', $resultname = '') {
        if ($xmlname) {
            $xml = \util\XMLUtil::changeArray2Xml ( $arr );
            $param [$xmlname] = $xml;
        } else {
            $param = $arr;
        }
        $data = array ();
        try {
            $id = $wsUrl . ':' . scp_get_thread_id ();
            $soapObject = self::fetchSoapInstance ( $id, $wsUrl );
            $out = $soapObject->__soapCall ( $func, array (
                    $param 
            ) );
            if (! $resultname) {
                $resultname = $func . 'Result';
            }
            $data = $out->{$resultname};
        } catch ( \SoapFault $fault ) {
            unset ( self::$soapInstance [$id] );
            $str = 'URL ' . $wsUrl . ' FUNC  ' . $func . ' NAME ' . $resultname . ' error ' . $fault->faultstring;
            soa_error ( $str, '[pcsoapinterfacefailed]' );
        }
        return $data;
    }
    /**
     * curl multi fetch
     *
     * @param array $urlarr            
     * @return multitype:
     * @since May 22, 2015 3:06:12 PM
     * @access
     *
     */
    public static function curlMultiFetch($urlarr = array()) {
        $result = $res = $ch = array ();
        $nch = 0;
        $mh = curl_multi_init ();
        foreach ( $urlarr as $nk => $url ) {
            $timeout = 5;
            $ch [$nch] = curl_init ();
            curl_setopt_array ( $ch [$nch], array (
                    CURLOPT_URL => $url,
                    CURLOPT_HEADER => false,
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_TIMEOUT => $timeout 
            ) );
            curl_multi_add_handle ( $mh, $ch [$nch] );
            ++ $nch;
        }
        /* wait for performing request */
        $running = null;
        do {
            $mrc = curl_multi_exec ( $mh, $running );
        } while ( CURLM_CALL_MULTI_PERFORM == $mrc );
        
        while ( $running > 0 && $mrc == CURLM_OK ) {
            if (curl_multi_select ( $mh, 0.5 ) > - 1) {
                do {
                    $mrc = curl_multi_exec ( $mh, $running );
                } while ( CURLM_CALL_MULTI_PERFORM == $mrc );
            }
        }
        if ($mrc != CURLM_OK) {
            error_log ( "CURL Data Error" );
        }
        $nch = 0;
        foreach ( $urlarr as $moudle => $node ) {
            if (($err = curl_error ( $ch [$nch] )) == '') {
                $res [$nch] = curl_multi_getcontent ( $ch [$nch] );
                $result [$moudle] = $res [$nch];
            } else {
                error_log ( "curl error" );
            }
            curl_multi_remove_handle ( $mh, $ch [$nch] );
            curl_close ( $ch [$nch] );
            ++ $nch;
        }
        curl_multi_close ( $mh );
        return $result;
    }
}
?>

